//author Sylvain Bertrand <digital.ragnarok@gmail.com>
//Protected by GNU Affero GPL v3 with some exceptions.
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <error.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>

#include <linux/types.h>

#include <alga/pixel_fmts.h>
#include <alga/amd/dce6/dce6.h>
#include <alga/amd/si/ioctl.h>

#define e(m,...) error(0,0,m,##__VA_ARGS__)
#define o(m,...) printf(m "\n",##__VA_ARGS__)
#define unsignedl unsigned long
#define unsignedll unsigned long long

int main(int argc, char *argv[])
{
  if(argc<5){
    e("missing arguments");
    return EXIT_FAILURE;
  }

  uint64_t fb_gpu_addr=strtoul(argv[1],NULL,16);
  uint64_t w=strtoul(argv[2],NULL,10);
  uint64_t h=strtoul(argv[3],NULL,10);
  uint64_t arg_pixel_fmt=strtoul(argv[4],NULL,10);
  uint8_t pixel_fmt=arg_pixel_fmt;

  errno=0; 
  int f=open("/dev/si0", O_RDWR);
  if(f==-1){
    e("open failed:%s",strerror(errno));
    return EXIT_FAILURE;
  }

  uint64_t sz=h*w*alga_pixel_fmts_sz[pixel_fmt];
 
  errno=0; 
  void *dma_buffer0=mmap(NULL,sz,PROT_READ|PROT_WRITE,MAP_SHARED,f,0);
  if(dma_buffer0==MAP_FAILED){
    e("unable to mmap a dma buffer0:%s",strerror(errno));
    return EXIT_FAILURE;
  }


  printf("dma_buffer0=%p\n",dma_buffer0);
 

  uint64_t *qw=dma_buffer0;
  uint64_t clr_qws=sz>>3;
  while(clr_qws--)
	*qw++=0x0000ff000000ff00;

  struct si_dma dma;
  struct si_dma_l2l *l2l=&dma.params.l2l;
  struct si_timeouts_info *t_info=&dma.t_info;
  dma.type=SI_DMA_TYPE_L2L;
  dma.dir=SI_DMA_TO_DEVICE;
  //we don't really care here lets put one seconde!
  t_info->ring.n_max=1;
  t_info->ring.us=1000000;
  t_info->fence.n_max=1;
  t_info->fence.us=1000000;
  l2l->src_addr=(uint64_t)dma_buffer0;
  l2l->dst_addr=fb_gpu_addr;
  l2l->sz=sz;
  unsignedl req=_IOW('d',SI_DMA,dma);
  errno=0;
  int r=ioctl(f,req,&dma);
  switch(r){
  case -1:
    e("dma l2l failed:%s",strerror(errno));
    return EXIT_FAILURE;
  case SI_RING_TIMEOUT:
    e("dma l2l failed:ring timeout");
    return EXIT_FAILURE;
  case SI_FENCE_TIMEOUT:
    e("dma l2l failed:fence timeout");
    return EXIT_FAILURE;
  }
  return EXIT_SUCCESS;
}
